import { message } from "antd";
import axios, {
  AxiosError,
  AxiosInstance,
  AxiosRequestConfig,
  AxiosRequestHeaders,
  AxiosResponse,
} from "axios";
import createAuthRefreshInterceptor from "axios-auth-refresh";
import { find } from "lodash";
import qs from "qs";

import { useSafeRouter } from "~/composables/use-safe-router";
import { useAuthStore } from "~/modules/auth/store/use-auth";
import { useTokenStore } from "~/modules/auth/store/use-token";
import { error } from "~/modules/shared/utils/common";

interface HttpApiClientConfig {
  url: string;
}

const createClientInstance = (config: HttpApiClientConfig): AxiosInstance => {
  const instance = axios.create({
    baseURL: config.url,
    withCredentials: false,
  });
  instance.defaults.headers.common = { "Accept-Language": "zh-CN" };
  instance.defaults.headers.post = {};
  instance.defaults.headers.put = {};
  instance.defaults.headers.patch = {};
  instance.defaults.headers.delete = {};
  instance.defaults.headers.get = {};
  instance.defaults.headers.head = {};
  return instance;
};

const loadHeaders = (config: AxiosRequestConfig): AxiosRequestHeaders => {
  const { hasToken, token } = useTokenStore.getState();
  const headers: AxiosRequestHeaders =
    (config.headers as AxiosRequestHeaders) ?? {};
  if (hasToken()) {
    headers["Authorization"] = `Bearer ${token}`;
  }
  return headers;
};

const setupRequestInterceptor = (client: AxiosInstance): void => {
  client.interceptors.request.use(
    (config: AxiosRequestConfig) => {
      config.headers = loadHeaders(config);
      return config;
    },
    (error: any) => {
      return Promise.reject(error);
    }
  );
};

interface HttpStatusErrorHandler {
  // eslint-disable-next-line no-unused-vars
  support(httpStatus: number): boolean;

  // eslint-disable-next-line no-unused-vars
  handle(error: AxiosError): void;
}

const HTTP_STATUS_ERROR_HANDLERS: HttpStatusErrorHandler[] = [
  {
    support(httpStatus: number): boolean {
      return httpStatus === 400;
    },
    handle(e: AxiosError) {
      error((e.response?.data as any).message);
      // console.log(error.message);
    },
  },
  {
    support(httpStatus: number): boolean {
      return httpStatus === 401;
    },
    // eslint-disable-next-line no-unused-vars
    async handle(error: AxiosError) {
      const { cleanToken } = useTokenStore.getState();
      const { goto } = useSafeRouter();
      cleanToken();
      await goto("/login");
    },
  },
  {
    support(httpStatus: number): boolean {
      return httpStatus >= 500;
    },
    handle(error: AxiosError) {
      console.log(error.message);
    },
  },
];

const handleResponseError = (error: AxiosError): void => {
  const httpStatus = error.response?.status ?? 500;
  const handler: HttpStatusErrorHandler | undefined = find(
    HTTP_STATUS_ERROR_HANDLERS,
    (_handler) => {
      return _handler.support(httpStatus);
    }
  );
  handler?.handle(error);
};

const setupResponseInterceptor = (client: AxiosInstance): void => {
  client.interceptors.response.use(
    (response: AxiosResponse) => response,
    async (error: AxiosError) => {
      handleResponseError(error);
      return Promise.reject(error);
    }
  );
};

const setupAuthRefreshInterceptor = (client: AxiosInstance): void => {
  createAuthRefreshInterceptor(
    client,
    async (failedRequest) => {
      if (failedRequest.response.data.code == 401) {
        return client.post("/api/refresh-token").then((response) => {
          const token: string = response.data.data;
          const { setToken } = useTokenStore.getState();
          setToken(token);
          failedRequest.response.config.headers[
            "Authorization"
          ] = `Bearer ${token}`;
          return Promise.resolve();
        });
      }

      const { signOut } = await useAuthStore();
      signOut();
      return Promise.reject(failedRequest.response.data.message);
    },
    {
      retryInstance: client,
      statusCodes: [401],
    }
  );
};

const setupInterceptors = (client: AxiosInstance): void => {
  setupRequestInterceptor(client);
  setupAuthRefreshInterceptor(client);
  setupResponseInterceptor(client);
};

export const createClient = (
  config: HttpApiClientConfig
): { client: AxiosInstance } => {
  const client = createClientInstance(config);
  setupInterceptors(client);
  return {
    client,
  };
};
